using System;
using System.Collections.Generic;
using System.Text;

namespace RapidHDL
{
    public class ExternalSignalInterface : Component
    {

        public NodeVector ClockIn;
        public NodeVector ResetIn;
        public NodeVector StartIn;
        public NodeVector WriteIn;
        public NodeVector WriteAddressIn;
        public NodeVector ReadAddressIn;

        public NodeVector DataIn;
        public NodeVector DataOut;

        //bool bClockIn = true; remove warning
        //bool bResetIn = true; remove warning
        bool bStartIn = false;

        public ClockSink WriteClock;
        
        public  ExternalSignalInterface(Component poParentComponent, string psName, int piWriteAddressWidth, int piReadAddressWidth, int piInputWidth, int piOutputWidth)
            : base(poParentComponent, psName)
        {
            
            StartIn = this.CreateNodeVector("START_I", 1);
            ClockIn = this.CreateNodeVector("CLK_I", 1);

            ResetIn = this.CreateNodeVector("RESET_I", 1);

            WriteIn = this.CreateNodeVector("WRITE_I", 1);

            WriteAddressIn = this.CreateNodeVector("WRITE_ADDR_I", piWriteAddressWidth);
            ReadAddressIn = this.CreateNodeVector("READ_ADDR_I", piReadAddressWidth);

            DataIn = this.CreateNodeVector("DATA_I", piInputWidth);
            DataOut = this.CreateNodeVector("DATA_O", piOutputWidth);
        }

        public override void GenerateStructure()
        {
            //RapidHardware.SystemInterface.AddSignalView("latched_data", DataIn);

            WriteClock = new ClockSink(this, "write_buff_in");
            WriteClock.ClockIn.Connection = WriteIn;

            System.Collections.ArrayList alSelectNodes = new System.Collections.ArrayList();

            if (RapidHardware.Structure.TopLevelComponent.MainClock != null)
            {
                RapidHardware.Structure.TopLevelComponent.MainClock.ClockIn.Connection = ClockIn;
            }

    

            /*this.RapidHardware.SystemInterface.AddSignalSource("MAIN_WRITE_I", 1, 0);
            this.RapidHardware.SystemInterface.AddSignalSource("MAIN_WRITE_ADDR_I", WriteAddressIn.Nodes.Count, 0);
            this.RapidHardware.SystemInterface.AddSignalSource("MAIN_READ_ADDR_I", ReadAddressIn.Nodes.Count, 0);
            this.RapidHardware.SystemInterface.AddSignalSource("MAIN_DATA_I", DataIn.Nodes.Count, 0);*/

            foreach (SignalSource oSignalSource in this.RapidHardware.SystemInterface.SignalSources.Values)
            {
                switch (oSignalSource.Name.ToUpper())
                {
                    case "MAIN_CLOCK":
                        oSignalSource.DataOut.Connection = ClockIn;
                        //bClockIn = true;
                        break;
                    //case "MAIN_RESET":
                    //    oSignalSource.DataOut.Connection = ResetIn;
                    //    bResetIn = true;
                    //    break;
                    case "MAIN_START":
                        oSignalSource.DataOut.Connection = StartIn;
                        bStartIn = true;
                        break;
                    /*case "MAIN_WRITE_I":
                        oSignalSource.DataOut.Connection = WriteIn;
                        break;
                    case "MAIN_WRITE_ADDR_I":
                        oSignalSource.ConnectNodeVector(WriteAddressIn, 0);
                        break;
                    case "MAIN_READ_ADDR_I":
                        oSignalSource.ConnectNodeVector(ReadAddressIn,0);
                        break;
                    case "MAIN_DATA_I":
                        oSignalSource.ConnectNodeVector(DataIn,0);
                        break;*/
                    default:
                        int iWidth = oSignalSource.Width;
                        int iHighBit = -1;
                        int iLowBit = -1;
                        int iMaxWidth = DataIn.Nodes.Count;
                        int iRegCount = 0;

                        while (iWidth > 0)
                        {
                            NodeVector nvSelectNode = new NodeVector(this, "sel_" + oSignalSource.Name + "_" + alSelectNodes.Count.ToString(), 1);
                            alSelectNodes.Add(nvSelectNode);
                            oSignalSource.AddressLines.Add(alSelectNodes.Count - 1);

                            iLowBit = iHighBit + 1;
                            iHighBit = iLowBit + iWidth - 1;
                            if (iWidth <= iMaxWidth)
                            {
                                iWidth = 0;
                            }
                            if (iWidth > iMaxWidth)
                            {
                                iHighBit = iLowBit + iMaxWidth - 1;
                                iWidth -= iMaxWidth;
                            }

                            iRegCount++;
                            RegisterComponent oSourceReg = new RegisterComponent(this,"in_reg_" + oSignalSource.Name + "_" + iRegCount.ToString(), WriteClock, iHighBit - iLowBit + 1, true, true);
                            NodeVector nvConnector = new NodeVector(this, oSignalSource.Name + "_" + iRegCount.ToString() + "_o", iHighBit - iLowBit + 1);
                            oSourceReg.InputNodes.ConnectSubset(DataIn, 0, nvConnector.Nodes.Count);
                            oSourceReg.OutputNodes.ConnectSubset(nvConnector, 0, nvConnector.Nodes.Count);
                            oSourceReg.EnableNodes.Connection = nvSelectNode;
                            nvConnector.ConnectSubset(oSignalSource.DataOut, iLowBit, iHighBit);
                            oSignalSource.InterfaceNodeVectors.Add(oSignalSource.InterfaceNodeVectors.Count, nvConnector);
                        }
                        break;
                }
            }

            //if (!bClockIn)
            //    this.CreateNodeVector("clock_sink", 1,NodeFlowType.Sink).Connection = ClockIn;

            //if (!bResetIn)
            //    this.CreateNodeVector("reset_sink", 1, NodeFlowType.Sink).Connection = ResetIn;

            if (!bStartIn)
                this.CreateNodeVector("start_sink", 1, NodeFlowType.Sink).Connection = StartIn;


            Decoder oDecoder = new Decoder(this, "addr_decode", Conversion.MinBitWidth(alSelectNodes.Count), alSelectNodes.Count);
            oDecoder.InputNodes.Connection  = WriteAddressIn;

            for (int idx = 0; idx < alSelectNodes.Count; idx++)
            {
                NodeVector nvSelectNode = (NodeVector)alSelectNodes[idx];
                Node oNode = (Node)nvSelectNode[0];
                oDecoder.OutputNodes[idx].Net.Connect(oNode.Net);
            }

            int iMuxPorts = 0;
            foreach (SignalView oSignalView in this.RapidHardware.SystemInterface.SignalViews.Values)
            {
                if (oSignalView.Name == "HELLO")
                    Console.WriteLine("Debug");

                iMuxPorts += (int)((double)oSignalView.Width / (double)DataOut.Nodes.Count + .99999999);
            }

            Mux oMux = new Mux(this, "out_mux", DataOut.Nodes.Count, iMuxPorts);
            oMux.SelectNodes.Connection = ReadAddressIn;
            oMux.OutputNodes.Connection = DataOut;
            int iMuxPort = 0;
            foreach (SignalView oSignalView in this.RapidHardware.SystemInterface.SignalViews.Values)
            {
                int iWidth = oSignalView.Width;
                int iHighBit = -1;
                int iLowBit = -1;
                int iMaxWidth = DataOut.Nodes.Count;

                if (oSignalView.Name == "HELLO")
                    Console.WriteLine("Debug");


                while (iWidth > 0)
                {
                    iLowBit = iHighBit + 1;
                    iHighBit = iLowBit + iWidth - 1;
                    if (iWidth <= iMaxWidth)
                    {
                        iWidth = 0;
                    }
                    if (iWidth > iMaxWidth)
                    {
                        iHighBit = iLowBit + iMaxWidth - 1;
                        iWidth -= iMaxWidth;
                    }

                    oMux.InputNodes[iMuxPort].ConnectSubset(oSignalView.DataView, iLowBit, iHighBit);
                    if (oSignalView.Name != ".TEST_BUS_READ_I")
                    {
                        Utility.SourceExtraBits(this, oMux.InputNodes[iMuxPort], NodeState.Undefined);
                        //oSignalView.Hidden = true;
                    }
                    oSignalView.AddressLines.Add(iMuxPort);
                    iMuxPort++;
                }
            }
            Utility.SinkExtraBits(this,DataIn);
            Utility.SinkExtraBits(this,ReadAddressIn);
            Utility.SinkExtraBits(this, WriteAddressIn);

            if (!RapidHardware.Settings.UseReset)
            {
                Utility.SinkExtraBits(this, ResetIn);
            }
        }
    }
}
